<template>
  <div class="tiptap-editor">
    <div v-if="menubar" class="tiptap-toolbar">
      <div class="toolbar-group">
        <button
          type="button"
          class="toolbar-btn"
          :class="{ 'is-active': editor.isActive('bold') }"
          @click="editor.chain().focus().toggleBold().run()"
        >
          <i class="el-icon-setting" />
          <span>粗体</span>
        </button>
        <button
          type="button"
          class="toolbar-btn"
          :class="{ 'is-active': editor.isActive('italic') }"
          @click="editor.chain().focus().toggleItalic().run()"
        >
          <i class="el-icon-edit" />
          <span>斜体</span>
        </button>
        <button
          type="button"
          class="toolbar-btn"
          :class="{ 'is-active': editor.isActive('bulletList') }"
          @click="editor.chain().focus().toggleBulletList().run()"
        >
          <i class="el-icon-menu" />
          <span>无序列表</span>
        </button>
        <button
          type="button"
          class="toolbar-btn"
          :class="{ 'is-active': editor.isActive('orderedList') }"
          @click="editor.chain().focus().toggleOrderedList().run()"
        >
          <i class="el-icon-finished" />
          <span>有序列表</span>
        </button>
        <button
          type="button"
          class="toolbar-btn"
          @click="addLink"
        >
          <i class="el-icon-link" />
          <span>链接</span>
        </button>
        <button
          type="button"
          class="toolbar-btn"
          @click="editor.chain().focus().undo().run()"
        >
          <i class="el-icon-refresh-left" />
          <span>撤销</span>
        </button>
        <button
          type="button"
          class="toolbar-btn"
          @click="editor.chain().focus().redo().run()"
        >
          <i class="el-icon-refresh-right" />
          <span>重做</span>
        </button>
        <button
          v-if="!disableImage"
          type="button"
          class="toolbar-btn"
          :class="{ 'is-loading': renewingLinks }"
          :disabled="renewingLinks"
          style="background-color: #1EB7E6; border-color: #1EB7E6; color: white;"
          @click="renewImageLinks"
        >
          <i class="el-icon-refresh" />
          <span>{{ renewingLinks ? '重新生成中...' : '重新生成链接' }}</span>
        </button>
      </div>
    </div>
    <div class="tiptap-content">
      <editor-content :editor="editor" />
    </div>
  </div>
</template>

<script>
import { Editor, EditorContent } from '@tiptap/vue-2'
import StarterKit from '@tiptap/starter-kit'
import Link from '@tiptap/extension-link'
import Image from '@tiptap/extension-image'
import TextAlign from '@tiptap/extension-text-align'
import TextStyle from '@tiptap/extension-text-style'
import Color from '@tiptap/extension-color'
import Highlight from '@tiptap/extension-highlight'
import { Post as UploadBase64Image } from '@/api/cms/image'
import { renewImageLinks } from '@/api/cms/image-renewal'

export default {
  name: 'TiptapEditor',
  components: {
    EditorContent
  },
  props: {
    value: {
      type: String,
      default: ''
    },
    height: {
      type: Number,
      default: 120
    },
    menubar: {
      type: Boolean,
      default: true
    },
    toolbar: {
      type: Array,
      default: () => []
    },
    autoFocus: {
      type: Boolean,
      default: false
    },
    disableImage: {
      type: Boolean,
      default: false
    }
  },
  data() {
    return {
      editor: null,
      renewingLinks: false
    }
  },
  watch: {
    value(val) {
      // 避免循环更新
      const isSame = this.editor.getHTML() === val
      if (isSame) {
        return
      }
      this.editor.commands.setContent(val, false)
    }
  },
  mounted() {
    // 根据disableImage属性决定是否包含Image扩展
    const extensions = [
      StarterKit,
      Link.configure({
        openOnClick: false,
        linkOnPaste: false // 关闭自动将粘贴的URL转为链接
      }),
      TextAlign.configure({
        types: ['heading', 'paragraph']
      }),
      TextStyle,
      Color,
      Highlight
    ]

    // 只有在不禁用图片时才添加Image扩展
    if (!this.disableImage) {
      extensions.push(Image)
    }

    this.editor = new Editor({
      extensions: extensions,
      content: this.value,
      autofocus: this.autoFocus,
      onUpdate: () => {
        this.$emit('input', this.editor.getHTML())
      }
    })

    // 只有在不禁用图片时才添加粘贴事件监听器
    if (!this.disableImage) {
      this.setupPasteHandler()
      this.setupRetryUploadHandler()
    }
  },
  beforeDestroy() {
    if (!this.disableImage) {
      this.cleanupPasteHandler()
      this.cleanupRetryUploadHandler()
    }
    if (this.editor) {
      this.editor.destroy()
    }
  },
  methods: {
    // 获取内容
    getContent() {
      return this.editor.getHTML()
    },

    // 设置内容
    setContent(content) {
      this.editor.commands.setContent(content, false)
    },

    // 清空内容
    clearContent() {
      this.editor.commands.clearContent()
    },

    // 聚焦编辑器
    focus() {
      this.editor.commands.focus()
    },

    // 添加链接
    addLink() {
      const url = window.prompt('请输入链接地址:')
      if (url) {
        this.editor.chain().focus().extendMarkRange('link').setLink({ href: url }).run()
      }
    },

    // 添加图片
    addImage() {
      const url = window.prompt('请输入图片地址:')
      if (url) {
        this.editor.chain().focus().setImage({ src: url }).run()
      }
    },

    // 设置粘贴事件处理器
    setupPasteHandler() {
      if (!this.editor) return

      const proseMirrorView = this.editor.view
      if (!proseMirrorView) return

      // 添加粘贴事件监听器
      proseMirrorView.dom.addEventListener('paste', this.handlePaste)
    },

    // 清理粘贴事件监听器
    cleanupPasteHandler() {
      if (!this.editor || !this.editor.view) return
      this.editor.view.dom.removeEventListener('paste', this.handlePaste)
    },

    // 设置重新上传点击事件
    setupRetryUploadHandler() {
      if (!this.editor) return

      const proseMirrorView = this.editor.view
      if (!proseMirrorView) return

      proseMirrorView.dom.addEventListener('click', this.handleRetryUpload)
    },

    // 清理重新上传事件监听器
    cleanupRetryUploadHandler() {
      if (!this.editor || !this.editor.view) return
      this.editor.view.dom.removeEventListener('click', this.handleRetryUpload)
    },

    // 处理重新上传点击
    handleRetryUpload(event) {
      const target = event.target
      if (target.tagName === 'IMG' && target.title === 'tiptap-upload-error') {
        // 获取错误图片的占位符ID
        const alt = target.getAttribute('alt')
        if (alt && alt.startsWith('error-')) {
          const placeholderId = alt.replace('error-', '')
          // 触发文件选择器
          const input = document.createElement('input')
          input.type = 'file'
          input.accept = 'image/*'
          input.onchange = (e) => {
            const file = e.target.files[0]
            if (file) {
              this.retryImageUpload(placeholderId, file)
            }
          }
          input.click()
        }
      }
    },

    // 重新上传图片
    async retryImageUpload(placeholderId, file) {
      // 重新显示上传状态
      const loadingImageSrc = 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(`
        <svg xmlns="http://www.w3.org/2000/svg" width="200" height="100" viewBox="0 0 200 100">
          <rect width="200" height="100" fill="#f0f0f0" stroke="#ddd" stroke-width="1"/>
          <text x="100" y="45" text-anchor="middle" font-family="Arial" font-size="12" fill="#666">
            重新上传中...
          </text>
          <rect x="20" y="60" width="160" height="8" fill="#e0e0e0" rx="4"/>
          <rect x="20" y="60" width="80" height="8" fill="#409eff" rx="4">
            <animate attributeName="width" values="0;160;0" dur="2s" repeatCount="indefinite"/>
          </rect>
        </svg>
      `)

      // 更新占位图片为上传状态
      this.updatePlaceholderImage(placeholderId, loadingImageSrc, 'tiptap-uploading-image')

      try {
        // 转换为base64
        const base64Data = await this.fileToBase64(file)

        // 调用上传接口
        const response = await UploadBase64Image({
          file_data: base64Data,
          directory_id: -1,
          keyword: ''
        })

        // 上传成功后替换占位图片
        this.replacePlaceholderImage(placeholderId, response.data.url)

        this.$notify({
          title: '成功',
          message: '图片上传成功',
          type: 'success',
          duration: 2000
        })
      } catch (error) {
        // 上传失败时显示错误状态
        this.showUploadError(placeholderId, error.message || '未知错误')

        this.$notify({
          title: '错误',
          message: '图片上传失败: ' + (error.message || '未知错误'),
          type: 'error',
          duration: 3000
        })
      }
    },

    // 更新占位图片
    updatePlaceholderImage(placeholderId, newSrc, newClass) {
      const proseMirrorView = this.editor.view
      const doc = proseMirrorView.state.doc

      doc.descendants((node, pos) => {
        if (node.type.name === 'image' &&
            (node.attrs.alt === `uploading-${placeholderId}` || node.attrs.alt === `error-${placeholderId}`)) {
          const tr = proseMirrorView.state.tr
          tr.setNodeMarkup(pos, null, {
            ...node.attrs,
            src: newSrc,
            alt: `uploading-${placeholderId}`,
            title: newClass
          })
          proseMirrorView.dispatch(tr)
          return false
        }
      })
    },

    // 处理粘贴事件
    async handlePaste(event) {
      const items = Array.from(event.clipboardData.items)
      const imageItems = items.filter(item => item.type.startsWith('image/'))

      if (imageItems.length === 0) return

      // 阻止默认粘贴行为
      event.preventDefault()
      event.stopPropagation()

      // 处理每个图片
      for (const item of imageItems) {
        await this.handleImageUpload(item)
      }
    },

    // 处理图片上传
    async handleImageUpload(item) {
      const file = item.getAsFile()
      if (!file) return

      // 生成唯一ID用于标识占位图片
      const placeholderId = `uploading-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`

      // 先插入占位图片，显示上传状态
      const loadingImageSrc = 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(`
        <svg xmlns="http://www.w3.org/2000/svg" width="200" height="100" viewBox="0 0 200 100">
          <rect width="200" height="100" fill="#f0f0f0" stroke="#ddd" stroke-width="1"/>
          <text x="100" y="45" text-anchor="middle" font-family="Arial" font-size="12" fill="#666">
            上传中...
          </text>
          <rect x="20" y="60" width="160" height="8" fill="#e0e0e0" rx="4"/>
          <rect x="20" y="60" width="80" height="8" fill="#409eff" rx="4">
            <animate attributeName="width" values="0;160;0" dur="2s" repeatCount="indefinite"/>
          </rect>
        </svg>
      `)

      // 在光标位置插入占位图片
      this.editor.chain().focus().setImage({
        src: loadingImageSrc,
        alt: `uploading-${placeholderId}`,
        title: 'tiptap-uploading-image'
      }).run()

      try {
        // 转换为base64
        const base64Data = await this.fileToBase64(file)

        // 调用上传接口 - 使用与TinyMCE相同的API
        const response = await UploadBase64Image({
          file_data: base64Data,
          directory_id: -1,
          keyword: ''
        })

        // 上传成功后替换占位图片
        this.replacePlaceholderImage(placeholderId, response.data.url)

        // 触发图片上传成功事件，传递图片信息给父组件
        this.$emit('image-uploaded', {
          url: response.data.url,
          size: file.size,
          placeholderId: placeholderId
        })

        this.$notify({
          title: '成功',
          message: '图片上传成功',
          type: 'success',
          duration: 2000
        })
      } catch (error) {
        // 上传失败时显示错误状态
        this.showUploadError(placeholderId, error.message || '未知错误')

        this.$notify({
          title: '错误',
          message: '图片上传失败: ' + (error.message || '未知错误'),
          type: 'error',
          duration: 3000
        })
      }
    },

    // 替换占位图片为真实图片
    replacePlaceholderImage(placeholderId, realImageUrl) {
      const proseMirrorView = this.editor.view
      const doc = proseMirrorView.state.doc
      let found = false

      doc.descendants((node, pos) => {
        if (node.type.name === 'image' &&
            node.attrs.alt === `uploading-${placeholderId}`) {
          console.log('找到占位图片，正在替换:', placeholderId, realImageUrl)
          const tr = proseMirrorView.state.tr
          tr.setNodeMarkup(pos, null, {
            src: realImageUrl,
            alt: '',
            title: ''
          })
          proseMirrorView.dispatch(tr)
          found = true
          return false // 停止遍历
        }
      })

      if (!found) {
        console.warn('未找到占位图片:', `uploading-${placeholderId}`)
        // 打印所有图片节点信息用于调试
        doc.descendants((node, pos) => {
          if (node.type.name === 'image') {
            console.log('现有图片节点:', node.attrs)
          }
        })
      }
    },

    // 显示上传错误状态
    showUploadError(placeholderId, errorMessage) {
      const errorImageSrc = 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(`
        <svg xmlns="http://www.w3.org/2000/svg" width="200" height="100" viewBox="0 0 200 100">
          <rect width="200" height="100" fill="#fef0f0" stroke="#f56c6c" stroke-width="1"/>
          <text x="100" y="35" text-anchor="middle" font-family="Arial" font-size="12" fill="#f56c6c">
            上传失败
          </text>
          <text x="100" y="55" text-anchor="middle" font-family="Arial" font-size="10" fill="#999">
            ${errorMessage.length > 20 ? errorMessage.substring(0, 20) + '...' : errorMessage}
          </text>
          <text x="100" y="75" text-anchor="middle" font-family="Arial" font-size="10" fill="#409eff" style="cursor: pointer">
            点击重新上传
          </text>
        </svg>
      `)

      const proseMirrorView = this.editor.view
      const doc = proseMirrorView.state.doc

      doc.descendants((node, pos) => {
        if (node.type.name === 'image' &&
            node.attrs.alt === `uploading-${placeholderId}`) {
          const tr = proseMirrorView.state.tr
          tr.setNodeMarkup(pos, null, {
            ...node.attrs,
            src: errorImageSrc,
            alt: `error-${placeholderId}`,
            title: 'tiptap-upload-error'
          })
          proseMirrorView.dispatch(tr)
          return false
        }
      })
    },

    // 文件转base64
    fileToBase64(file) {
      return new Promise((resolve, reject) => {
        const reader = new FileReader()
        reader.readAsDataURL(file)
        reader.onload = () => resolve(reader.result)
        reader.onerror = error => reject(error)
      })
    },

    // 移除最后插入的图片（用于将图片作为附件而不是内容）
    removeLastImage() {
      if (!this.editor) return

      const proseMirrorView = this.editor.view
      const doc = proseMirrorView.state.doc
      let lastImagePos = null

      // 查找最后一个图片节点
      doc.descendants((node, pos) => {
        if (node.type.name === 'image') {
          lastImagePos = pos
        }
      })

      // 如果找到了图片，删除它
      if (lastImagePos !== null) {
        const tr = proseMirrorView.state.tr
        tr.delete(lastImagePos, lastImagePos + 1)
        proseMirrorView.dispatch(tr)
      }
    },

    // 重新生成图片链接
    async renewImageLinks() {
      if (this.renewingLinks) return

      const currentContent = this.editor.getHTML()
      if (!currentContent.includes('<img')) {
        this.$message.info('当前内容中没有图片需要重新生成链接')
        return
      }

      this.renewingLinks = true

      try {
        const response = await renewImageLinks({ content: currentContent })

        if (response.success) {
          const { renewed_content, renewed_links, failed_links } = response.data

          if (renewed_links && renewed_links.length > 0) {
            // 更新编辑器内容
            this.editor.commands.setContent(renewed_content)

            // 显示成功消息
            this.$notify({
              title: '成功',
              message: `成功重新生成 ${renewed_links.length} 个图片链接`,
              type: 'success',
              duration: 3000
            })

            // 触发内容更新事件
            this.$emit('input', renewed_content)

            // 如果有失败的链接，显示警告
            if (failed_links && failed_links.length > 0) {
              this.$notify({
                title: '警告',
                message: `有 ${failed_links.length} 个链接重新生成失败`,
                type: 'warning',
                duration: 3000
              })
            }
          } else {
            this.$message.info('没有链接需要重新生成')
          }
        } else {
          this.$notify({
            title: '错误',
            message: response.msg || '重新生成链接失败',
            type: 'error',
            duration: 3000
          })
        }
      } catch (error) {
        console.error('重新生成图片链接失败:', error)
        this.$notify({
          title: '错误',
          message: '重新生成链接失败: ' + (error.message || '网络错误'),
          type: 'error',
          duration: 3000
        })
      } finally {
        this.renewingLinks = false
      }
    }
  }
}
</script>

<style scoped>
.tiptap-editor {
  border: 1px solid #dcdfe6;
  border-radius: 4px;
  background-color: #fff;
}

.tiptap-toolbar {
  border-bottom: 1px solid #dcdfe6;
  padding: 8px 12px;
  background-color: #f5f7fa;
}

.toolbar-group {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
}

.toolbar-btn {
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 6px 10px;
  border: 1px solid #dcdfe6;
  border-radius: 3px;
  background-color: #fff;
  color: #606266;
  font-size: 12px;
  cursor: pointer;
  transition: all 0.2s;
}

.toolbar-btn:hover {
  background-color: #ecf5ff;
  color: #409eff;
  border-color: #c6e2ff;
}

.toolbar-btn.is-active {
  background-color: #409eff;
  color: #fff;
  border-color: #409eff;
}

.tiptap-content {
  padding: 12px;
  min-height: 120px;
}

/* 编辑器内容样式 */
.tiptap-content >>> .ProseMirror {
  outline: none;
  min-height: 100px;
  line-height: 1.6;
}

.tiptap-content >>> .ProseMirror p {
  margin: 0 0 8px 0;
}

.tiptap-content >>> .ProseMirror ul,
.tiptap-content >>> .ProseMirror ol {
  margin: 8px 0;
  padding-left: 20px;
}

.tiptap-content >>> .ProseMirror li {
  margin: 4px 0;
}

.tiptap-content >>> .ProseMirror a {
  color: #409eff;
  text-decoration: underline;
}

.tiptap-content >>> .ProseMirror img {
  max-width: 100%;
  height: auto;
  border-radius: 4px;
  margin: 8px 0;
}

/* 上传中的图片样式 */
.tiptap-content >>> .ProseMirror img[title="tiptap-uploading-image"] {
  opacity: 0.8;
  border: 2px dashed #409eff;
  background-color: #f0f9ff;
}

/* 上传失败的图片样式 */
.tiptap-content >>> .ProseMirror img[title="tiptap-upload-error"] {
  opacity: 0.9;
  border: 2px dashed #f56c6c;
  background-color: #fef0f0;
  cursor: pointer;
}

.tiptap-content >>> .ProseMirror img[title="tiptap-upload-error"]:hover {
  opacity: 1;
  border-color: #f56c6c;
}

.tiptap-content >>> .ProseMirror strong {
  font-weight: bold;
}

.tiptap-content >>> .ProseMirror em {
  font-style: italic;
}

.tiptap-content >>> .ProseMirror mark {
  background-color: #ffeb3b;
  color: #000;
  padding: 2px 4px;
  border-radius: 2px;
}

/* 占位符样式 */
.tiptap-content >>> .ProseMirror.ProseMirror-focused {
  outline: none;
}

.tiptap-content >>> .ProseMirror p.is-editor-empty:first-child::before {
  content: attr(data-placeholder);
  float: left;
  color: #adb5bd;
  pointer-events: none;
  height: 0;
}
</style>
